package org.yeasy.report.util;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.yeasy.common.util.CommonBeanUtil;
import org.yeasy.report.bean.ColumnSetting;
import org.yeasy.report.constant.ColumnType;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * The type Tree report util.
 *
 * @author yangyishe
 * @date 2022年09月14日 15:41
 */
public class TreeReportUtil {

    /**
     * Calc key 2 offspring map.提供另一种更有效率也更安全的计算后代编码的方法
     * 计算原则是通过标准level实现
     * 次级计算方法,一般不直接使用
     *
     * @param key2ParentKeyMap the key 2 parent key map
     * @param includeSelf      the include self
     * @return the map
     * @author yangyishe
     * @date 2022年08月30日 10:11
     */
    public static Map<String, Set<String>> calcKey2Offspring(Map<String, String> key2ParentKeyMap, boolean includeSelf) {
        Map<String, String> mapKep2ParentKeyClone = ObjectUtil.clone(key2ParentKeyMap);

        // 先获取祖先数据,并将祖先数据的level设置为0
        Set<String> setParentKeyAll = new HashSet<>(mapKep2ParentKeyClone.values());
        Set<String> setAncestorKey = setParentKeyAll.stream().filter(s -> !mapKep2ParentKeyClone.containsKey(s)).collect(Collectors.toSet());
        Map<String, Integer> mapKey2Level = new HashMap<>();
        for (String s : setAncestorKey) {
            mapKey2Level.put(s, 0);
        }

        // 再逐次递归,逐渐删除
        Set<String> setRemoveKey = new HashSet<>();
        while (true) {
            int intLastSize = mapKep2ParentKeyClone.size() - setRemoveKey.size();
            for (Map.Entry<String, String> key2ParentKey : mapKep2ParentKeyClone.entrySet()) {
                String strKey = key2ParentKey.getKey();
                String strParentKey = key2ParentKey.getValue();
                if (setRemoveKey.contains(strKey)) {
                    continue;
                }
                if (mapKey2Level.containsKey(strParentKey)) {
                    mapKey2Level.put(strKey, mapKey2Level.get(strParentKey) + 1);
                    setRemoveKey.add(strKey);
                }
            }
            // 注:两种情况下会直接跳出:1.没有数据可供使用了;2.有数据但是循环数据.
            if (intLastSize == mapKep2ParentKeyClone.size() - setRemoveKey.size()) {
                break;
            }
        }

        // 根据level分别创建map
        List<Map.Entry<String, String>> lstEntrySortByLevel = key2ParentKeyMap.entrySet().stream()
                .filter(o -> mapKey2Level.get(o.getKey()) != null)
                .sorted(Comparator.comparing(o -> mapKey2Level.get(o.getKey())))
                .collect(Collectors.toList());
        Map<String, Set<String>> mapResult = new HashMap<>();
        for (Map.Entry<String, String> key2ParentKey : lstEntrySortByLevel) {
            String strKey = key2ParentKey.getKey();
            String strParentKey = key2ParentKey.getValue();
            mapResult.putIfAbsent(strParentKey, new HashSet<>());
            do {
                mapResult.get(strParentKey).add(strKey);
                strParentKey = key2ParentKeyMap.get(strParentKey);
            } while (strParentKey != null);
            if (includeSelf) {
                mapResult.put(strKey, new HashSet<>());
                mapResult.get(strKey).add(strKey);
            }
        }

        return mapResult;
    }

    /**
     * Calc leaf 2 full level list.末级数据转全level
     * 现在允许分组情况出现
     *
     * @param <T>               the type parameter
     * @param leafList          the leaf list 默认leaf数据均已被fill了
     * @param baseList          the base list
     * @param columnSettingList the report column setting list
     *                          GROUP 其中至少有一个包含parentField,作为父子关系的引导.其余内容,则作为分组之用,譬如月份标记.
     *                          KEEP 作为保留字段,根据leaf的内容确定.主要用于多级分组情况下,拷贝clone的内容.默认情况是根据baseList的内容确定的.
     *                          CALC 作为求和计算字段.
     * @return the list
     * @author yangyishe
     * @date 2022年08月30日 11:09
     */
    public static <T extends Serializable> List<T> calcLeaf2FullLevel(List<T> leafList, List<T> baseList, List<ColumnSetting> columnSettingList) {
        // 计算末级全级对照关系数据
        List<ColumnSetting> lstParentSetting = columnSettingList.stream()
                .filter(o -> CharSequenceUtil.isNotEmpty(o.getParentField()))
                .collect(Collectors.toList());
        if(lstParentSetting.isEmpty()){
            throw new RuntimeException("TreeReportUtil calcLeaf2FullLevel must have one setting with parentField.");
        }
        ColumnSetting mParentSetting = lstParentSetting.get(0);
        Map<String, String> mapKey2ParentKey = new HashMap<>();
        for (T obj : baseList) {
            String strKey = CommonBeanUtil.invokeGetter(obj, mParentSetting.getField());
            String strParentKey = CommonBeanUtil.invokeGetter(obj, mParentSetting.getParentField());
            mapKey2ParentKey.put(strKey, strParentKey);
        }
        Map<String, Set<String>> mapKey2Offspring = calcKey2Offspring(mapKey2ParentKey, true);

        // 不包含核心key的groupSetting
        List<ColumnSetting> lstGroupSettingWithoutCore = columnSettingList.stream()
                .filter(o -> o.getType().equals(ColumnType.GROUP))
                .filter(o-> CharSequenceUtil.isEmpty(o.getParentField()))
                .collect(Collectors.toList());
        Function<T, String> calcUniqueMark = ColumnUtil.calc2GroupKeyFunc(lstGroupSettingWithoutCore);

        List<String> lstCalcField = ColumnUtil.calc2FieldList(columnSettingList, ColumnType.CALC);
        List<String> lstKeepField = ColumnUtil.calc2FieldList(columnSettingList, ColumnType.KEEP);
        List<T> lstResult = new ArrayList<>();
        for (T mBase : baseList) {
            String strKey = CommonBeanUtil.invokeGetter(mBase, mParentSetting.getField());
            Set<String> setKey = mapKey2Offspring.get(strKey);
            List<T> lstLeafEach = leafList.stream().filter(o -> {
                String strLeafKey = CommonBeanUtil.invokeGetter(o, mParentSetting.getField());
                return setKey.contains(strLeafKey);
            }).collect(Collectors.toList());

            Map<String, List<T>> mapUniqueMark2LeafList = new HashMap<>();
            for (T mLeaf : lstLeafEach) {
                String strUniqueMark = calcUniqueMark.apply(mLeaf);
                mapUniqueMark2LeafList.putIfAbsent(strUniqueMark, new ArrayList<>());
                mapUniqueMark2LeafList.get(strUniqueMark).add(mLeaf);
            }

            for (List<T> leafListEachEach : mapUniqueMark2LeafList.values()) {
                T mLeafFirst = leafListEachEach.get(0);
                T mBaseClone = ObjectUtil.clone(mBase);
                for (ColumnSetting mCloneSetting : lstGroupSettingWithoutCore) {
                    Object mGroupVal = CommonBeanUtil.invokeGetter(mLeafFirst, mCloneSetting.getField());
                    CommonBeanUtil.invokeSetter(mBaseClone, mCloneSetting.getField(), mGroupVal);
                }
                for (String keepField : lstKeepField) {
                    Object mKeepVal = CommonBeanUtil.invokeGetter(mLeafFirst, keepField);
                    if (mKeepVal != null) {
                        CommonBeanUtil.invokeSetter(mBaseClone, keepField, mKeepVal);
                    }
                }
                for (String strCalcField : lstCalcField) {
                    BigDecimal decInit = BigDecimal.ZERO;
                    for (T mLeaf : leafListEachEach) {
                        BigDecimal decEach = CommonBeanUtil.invokeGetter(mLeaf, strCalcField);
                        if (decEach != null) {
                            decInit = decInit.add(decEach);
                        }
                    }
                    CommonBeanUtil.invokeSetter(mBaseClone, strCalcField, decInit);
                }
                lstResult.add(mBaseClone);
            }

        }

        return lstResult;
    }
}
